package org.demo.ui;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.DefaultCaret;

import org.demo.utils.ComUtils;
import org.demo.utils.Config;
import org.demo.utils.OnClickListener;
import org.demo.view.IReadBackView;

public class MainFrame extends BaseFrame implements IReadBackView {

	private static final long serialVersionUID = -7208400958409923167L;

	private JTextField mPortTextField;
	private JTextField mProgrammerPathTextField;
	private JTextField mSearchPathTextField;
	private JTextField mOutputPathTextField;
	private JTextField mRawProgramPathTextField;
	private JComboBox<String> mDeviceTypeComboBox;
	private JProgressBar mProgressBar;
	private JTextArea mLogView;
	private JButton mExecuteButton;

	private JLabel selectRawProgramTipsLabel;

	private OnClickListener mListener;
	private TextChangeListener mTextChangeListener;

	private Config mConfig;

	private SimpleDateFormat df = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss");

	public MainFrame() {
		super();
		setTitle(TITLE);
		initMenu();
		setSize(800, 640);
		setLocationRelativeTo(null);
		setVisible(true);
		addWindowListener(new WindowAdapter() {

			@Override
			public void windowOpened(WindowEvent e) {
				super.windowOpened(e);
				mExecuteButton.requestFocus();
			}

			@Override
			public void windowClosing(WindowEvent e) {
				saveConfig();
				System.exit(0);
			}

		});
	}

	@Override
	protected void initConfig() {
		File file = new File("config.ini");
		mConfig = new Config(file.getAbsolutePath());
	}

	private void initMenu() {
		JMenuBar menuBar = new JMenuBar();
		menuBar.setPreferredSize(new Dimension(-1, 24));
		JMenu mFileMenu = new JMenu("File");
		JMenu mToolsMenu = new JMenu("Tools");
		JMenu mSettingsMenu = new JMenu("Settings");
		JMenu mHelpMenu = new JMenu("Help");
		menuBar.add(mFileMenu);
		menuBar.add(mToolsMenu);
		menuBar.add(mSettingsMenu);
		menuBar.add(mHelpMenu);

		JMenuItem mFileItem = new JMenuItem("Exit");
		mFileItem.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		});
		mFileMenu.add(mFileItem);

		JMenuItem mToolItem = new JMenuItem("XML Edit Tool");
		mToolsMenu.add(mToolItem);

		JMenuItem mSettingsItem = new JMenuItem("Configuration");
		mSettingsItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				new ConfigurationFrame();
			}
		});
		mSettingsMenu.add(mSettingsItem);

		JMenuItem mHelpItem = new JMenuItem("Help Topic");
		mHelpItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				new HelpFrame();
			}
		});
		mHelpMenu.add(mHelpItem);
		JMenuItem mAboutItem = new JMenuItem("About");
		mAboutItem.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				new AboutFrame();
			}
		});
		mHelpMenu.add(mAboutItem);

		this.setJMenuBar(menuBar);
	}

	@Override
	protected void initLayout() {
		this.setLayout(null);
		JLabel portLabel = new JLabel("COM Port", JLabel.CENTER);
		portLabel.setFont(titleFont);
		portLabel.setBounds(20, 10, 120, 30);
		this.add(portLabel);
		mPortTextField = new JTextField(mConfig.get(Config.KEY_COM_PORT));
		mPortTextField.setBounds(160, 10, 480, 30);
		mPortTextField.getDocument().addDocumentListener(new TextChangeListener());
		this.add(mPortTextField);
		JButton selectPortButton = new JButton("Select Port");
		selectPortButton.setBounds(660, 10, 120, 30);
		selectPortButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				mPortTextField.setText(ComUtils.getComPort());
			}
		});
		this.add(selectPortButton);

		JLabel selectProgrammerLabel = new JLabel("Select Programmer");
		selectProgrammerLabel.setFont(titleFont);
		selectProgrammerLabel.setBounds(20, 60, 760, 30);
		this.add(selectProgrammerLabel);
		JLabel programmerPathLabel = new JLabel("Programmer Path");
		programmerPathLabel.setBounds(20, 100, 120, 30);
		this.add(programmerPathLabel);
		mProgrammerPathTextField = new JTextField(mConfig.get(Config.KEY_PROGRAMMER_PATH));
		mProgrammerPathTextField.setBounds(160, 100, 480, 30);
		this.add(mProgrammerPathTextField);

		JLabel searchPathLabel = new JLabel("Search Path");
		searchPathLabel.setBounds(20, 140, 120, 30);
		this.add(searchPathLabel);
		mSearchPathTextField = new JTextField(mConfig.get(Config.KEY_SEARCH_PATH));
		mSearchPathTextField.setBounds(160, 140, 620, 30);
		this.add(mSearchPathTextField);

		JLabel outputPathLabel = new JLabel("Output Path");
		outputPathLabel.setBounds(20, 180, 120, 30);
		this.add(outputPathLabel);
		mOutputPathTextField = new JTextField(mConfig.get(Config.KEY_OUTPUT_PATH));
		mOutputPathTextField.setBounds(160, 180, 620, 30);
		this.add(mOutputPathTextField);

		JButton browseProgrammerButton = new JButton("Browse");
		browseProgrammerButton.setBounds(660, 100, 120, 30);
		browseProgrammerButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				browseProgrammer();
			}
		});
		this.add(browseProgrammerButton);

		JLabel selectRawProgramLabel = new JLabel("Select RawProgram");
		selectRawProgramLabel.setFont(titleFont);
		selectRawProgramLabel.setBounds(20, 230, 170, 30);
		this.add(selectRawProgramLabel);

		selectRawProgramTipsLabel = new JLabel();
		selectRawProgramTipsLabel.setBounds(190, 230, 590, 30);
		selectRawProgramTipsLabel.setVisible(false);
		selectRawProgramTipsLabel.setForeground(Color.RED);
		this.add(selectRawProgramTipsLabel);

		JLabel rawProgramPathLabel = new JLabel("RawProgram Path");
		rawProgramPathLabel.setBounds(20, 270, 120, 30);
		this.add(rawProgramPathLabel);
		mRawProgramPathTextField = new JTextField(mConfig.get(Config.KEY_RAWPROGRAMM_PATH));
		mRawProgramPathTextField.setBounds(160, 270, 480, 30);
		this.add(mRawProgramPathTextField);
		JButton loadXMLButton = new JButton("Load XML");
		loadXMLButton.setBounds(660, 270, 120, 30);
		loadXMLButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				loadXML();
			}

		});
		this.add(loadXMLButton);

		mProgressBar = new JProgressBar(1, 100);
		mProgressBar.setBounds(20, 310, 620, 30);
		this.add(mProgressBar);
		mExecuteButton = new JButton("Execute");
		mExecuteButton.setBounds(660, 310, 120, 30);
		mExecuteButton.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				if (mListener != null) {
					mListener.onClick(e);
				}
			}
		});
		isExecutable();
		this.add(mExecuteButton);

		JLabel statusLabel = new JLabel("Status");
		statusLabel.setFont(titleFont);
		statusLabel.setBounds(20, 350, 760, 30);
		this.add(statusLabel);

		mLogView = new JTextArea();
		mLogView.setLineWrap(true);
		mLogView.setFocusable(false);
		DefaultCaret caret = (DefaultCaret) mLogView.getCaret();
		caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); // set auto scroll
		JScrollPane scrollPane = new JScrollPane(mLogView);
		scrollPane.setBounds(20, 390, 760, 180);
		this.add(scrollPane);

		JLabel deviceTypeLabel = new JLabel("Device Type");
		deviceTypeLabel.setFont(titleFont);
		deviceTypeLabel.setBounds(550, 350, 90, 30);
		this.add(deviceTypeLabel);
		mDeviceTypeComboBox = new JComboBox<>();
		mDeviceTypeComboBox.addItem("EMMC");
		mDeviceTypeComboBox.addItem("UFS");
		mDeviceTypeComboBox.setBounds(660, 350, 120, 30);
		String index = mConfig.get(Config.KEY_DEVICE_TYPE);
		mDeviceTypeComboBox.setSelectedIndex(index == null ? 0 : Integer.parseInt(index));
		this.add(mDeviceTypeComboBox);

		mTextChangeListener = new TextChangeListener();
		mPortTextField.getDocument().addDocumentListener(mTextChangeListener);
		mProgrammerPathTextField.getDocument().addDocumentListener(mTextChangeListener);
		mSearchPathTextField.getDocument().addDocumentListener(mTextChangeListener);
		mOutputPathTextField.getDocument().addDocumentListener(mTextChangeListener);
		mRawProgramPathTextField.getDocument().addDocumentListener(mTextChangeListener);
	}

	private void browseProgrammer() {
		JFileChooser chooser = new JFileChooser();
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		if (!mProgrammerPathTextField.getText().isEmpty()) {
			chooser.setCurrentDirectory(new File(mProgrammerPathTextField.getText()).getParentFile());
		}
		chooser.setFileFilter(new FileFilter() {

			@Override
			public String getDescription() {
				return "prog_emmc_firehose_xxxx_ddr.mbn;prog_firehose_ddr.elf";
			}

			@Override
			public boolean accept(File f) {
				if (f.isDirectory()) {
					return true;
				}
				return (f.getName().endsWith(".mbn") || f.getName().endsWith(".elf"))
						&& f.getName().contains("firehose");
			}
		});
		chooser.showOpenDialog(this);
		File file = chooser.getSelectedFile();
		if (file != null) {
			mProgrammerPathTextField.setText(file.getAbsolutePath());
			mSearchPathTextField.setText(file.getParent());
			String output = file.getParent() + "\\output_" + df.format(new Date());
			mOutputPathTextField.setText(output);
		}
	}

	private void loadXML() {
		JFileChooser chooser = new JFileChooser();
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		chooser.setMultiSelectionEnabled(true);
		if (!mSearchPathTextField.getText().isEmpty()) {
			chooser.setCurrentDirectory(new File(mSearchPathTextField.getText()));
		}
		chooser.setFileFilter(new FileFilter() {

			@Override
			public String getDescription() {
				return "rawprogram_unsparse.xml";
			}

			@Override
			public boolean accept(File f) {
				if (f.isDirectory()) {
					return true;
				}
				return f.getName().contains("rawprogram") && f.getName().endsWith("xml");
			}
		});
		chooser.showOpenDialog(this);
		File[] files = chooser.getSelectedFiles();
		if (files != null && files.length > 0) {
			if (!files[0].getParent().equals(mSearchPathTextField.getText())) {
				selectRawProgramTipsLabel
						.setText("warning: search path and rawprogram path mismatch, it may cause a error");
				selectRawProgramTipsLabel.setVisible(true);
			} else {
				selectRawProgramTipsLabel.setText("");
				selectRawProgramTipsLabel.setVisible(false);
			}
			StringBuilder builder = new StringBuilder();
			if (files.length == 1) {
				builder.append(files[0].getName());
			} else {
				for (int i = 0; i < files.length; i++) {
					builder.append(files[i].getName());
					if (i != files.length - 1) {
						builder.append(",");
					}
				}
			}
			mRawProgramPathTextField.setText(builder.toString());
		}
	}

	public void setOnClickListener(OnClickListener listener) {
		mListener = listener;
	}

	@Override
	public String getQSaharaServerPath() {
		return getQSaharaServer();
	}

	@Override
	public String getFireHoseLoaderPath() {
		return getFireHoseLoader();
	}

	@Override
	public String getProgrammerPath() {
		return mProgrammerPathTextField.getText().trim();
	}

	@Override
	public String getRawprammPath() {
		return mRawProgramPathTextField.getText().trim();
	}

	@Override
	public String getSearchPath() {
		return mSearchPathTextField.getText().trim();
	}

	@Override
	public String getOutputPath() {
		return mOutputPathTextField.getText().trim();
	}

	@Override
	public String getDeviceType() {
		return ((String) mDeviceTypeComboBox.getSelectedItem()).trim();
	}

	@Override
	public String getCOMPort() {
		return mPortTextField.getText().trim();
	}

	@Override
	public void updateLog(String message) {
		mLogView.append(message);
		mLogView.append("\r\n");
	}

	@Override
	public void updateProgress(int progress) {
		mProgressBar.setValue(progress);
	}

	@Override
	public void success() {
		JOptionPane.showMessageDialog(null, "Done", "Result", JOptionPane.INFORMATION_MESSAGE);
	}

	@Override
	public void failure(String error) {
		JOptionPane.showMessageDialog(null, error, "Result", JOptionPane.ERROR_MESSAGE);
	}
	
	@Override
	public void enableExecute() {
		mExecuteButton.setEnabled(true);
	}

	@Override
	public void disableExecute() {
		mExecuteButton.setEnabled(false);
	}
	
	@Override
	public boolean isReset() {
		return mSettingConfig.get(Config.KEY_RESET_AFTER_DOWNLOAD).equals("1");
	}
	

	@Override
	public boolean isUseVerbose() {
		return mSettingConfig.get(Config.KEY_USE_VERBOSE).equals("1");
	}

	private void saveConfig() {
		mConfig.put(Config.KEY_COM_PORT, getCOMPort());
		mConfig.put(Config.KEY_PROGRAMMER_PATH, getProgrammerPath());
		mConfig.put(Config.KEY_SEARCH_PATH, getSearchPath());
		mConfig.put(Config.KEY_OUTPUT_PATH, getOutputPath());
		mConfig.put(Config.KEY_RAWPROGRAMM_PATH, getRawprammPath());
		mConfig.put(Config.KEY_DEVICE_TYPE, getDeviceTypeIndex());
		mConfig.save();
	}

	private String getDeviceTypeIndex() {
		return mDeviceTypeComboBox.getSelectedIndex() + "";
	}

	private class TextChangeListener implements DocumentListener {

		@Override
		public void insertUpdate(DocumentEvent e) {
			isExecutable();
		}

		@Override
		public void removeUpdate(DocumentEvent e) {
			isExecutable();
		}

		@Override
		public void changedUpdate(DocumentEvent e) {
			isExecutable();
		}

	}

	private void isExecutable() {
		if (getCOMPort().isEmpty() || getProgrammerPath().isEmpty() || getSearchPath().isEmpty()
				|| getOutputPath().isEmpty() || getRawprammPath().isEmpty()) {
			mExecuteButton.setEnabled(false);
		} else {
			mExecuteButton.setEnabled(true);
		}
	}

}
